package com.wenlin.myproduction.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.ColorInt;
import androidx.annotation.Nullable;

import com.wenlin.myproduction.R;
import com.wenlin.myproduction.bean.Block;
import com.wenlin.myproduction.util.Checker;
import com.wenlin.myproduction.util.LogUtils;
import com.wenlin.myproduction.util.TimeUtil;
import com.wenlin.myproduction.util.ToastUtils;
import com.wenlin.myproduction.util.UI;

import java.util.ArrayList;
import java.util.List;

/**
 * 回溯法遍历破解数独
 */
public class ShuDuView2 extends View {
    private static final float RADIUS = 5;
    private static final String[] rows = {"1", "2", "3", "4", "5", "6", "7", "8", "9"};
    private Paint paint;
    private TextPaint textPaint, textPaintBold;
    private Rect rect;
    private float blockLong;
    @ColorInt
    private int colorWhite = UI.getColor(R.color.myWhite);
    @ColorInt
    private int colorBlack = UI.getColor(R.color.myTitle);
    @ColorInt
    private int colorGray = UI.getColor(R.color.myGray);
    @ColorInt
    private int colorBlue = UI.getColor(R.color.myBlue);
    private int current;
    private int[][] arrTitle = new int[9][9];
    private int[][] arrAnswer = new int[9][9];
    private boolean editMode;

    public ShuDuView2(Context context) {
        this(context, null);
    }

    public ShuDuView2(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    /**
     * 初始化
     */
    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        paint.setColor(colorBlack);

        textPaint = new TextPaint();
        textPaint.setAntiAlias(true);
        textPaint.setDither(true);
        textPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        textPaint.setColor(colorBlack);
        textPaint.setTextSize(UI.getPx(R.dimen.wpx40));

        textPaintBold = new TextPaint();
        textPaintBold.setAntiAlias(true);
        textPaintBold.setDither(true);
        textPaintBold.setStyle(Paint.Style.FILL_AND_STROKE);
        textPaintBold.setColor(colorBlack);
        textPaintBold.setTextSize(UI.getPx(R.dimen.wpx80));

        rect = new Rect();
        setBlocks("");
    }

    /**
     * 设置是否可编辑
     *
     * @param canEdit 可否编辑
     */
    public void setEdit(boolean canEdit) {
        editMode = canEdit;
    }

    /**
     * 设置题目
     *
     * @param str 题目
     */
    public void setPuzzle(String str) {
        if (str.length() == 81) {
            setBlocks(str);
        }
    }

    /**
     * 设置格子
     *
     * @param str 题目
     */
    private void setBlocks(String str) {
        //默认设置一道困难难度的题目
        if (str.length() != 81) str = "009748000"
                + "700000000"
                + "020109000"
                + "007000240"
                + "064010590"
                + "098000300"
                + "000803020"
                + "000000006"
                + "000275900";
        //解析成数组
        for (int i = 0; i < 9; i++) {
            String rowStr = str.substring(i * 9, i * 9 + 9);
            for (int j = 0; j < 9; j++) {
                String s = rowStr.substring(j, j + 1);
                int num = Integer.parseInt(s);
                arrTitle[i][j] = num;
                arrAnswer[i][j] = num;
            }
        }
        invalidate();
    }

    /**
     * 获取位置数组
     *
     * @param arrFlag 位置的二维数组
     * @return 所求
     */
    private Block[][] getLocationList(int[][] arrFlag) {
        Block[][] arr = new Block[9][9];
        boolean flag = true;
        Block temp = new Block(-1, -1);
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (arrFlag[i][j] == 1) {
                    arr[i][j] = new Block(-1, -1);
                } else if (flag) {
                    arr[i][j] = new Block(i, j);
                    temp = new Block(i, j);
                    flag = false;
                } else {
                    arr[i][j] = temp;
                    temp = new Block(i, j);
                }
            }
        }
        return arr;
    }

    /**
     * 破解
     */
    private void figureOut() {
        int[][] arrFlag = new int[9][9];
        List<Integer>[][] invalid_init = getInvalidMat();
        List<Integer>[][] invalid = getInvalidMat();
        //得到标志矩阵，用来判断是否需要参与试错
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                int num = arrTitle[i][j];
                if (num > 0) {
                    arrFlag[i][j] = 1;
                } else {
                    arrFlag[i][j] = 0;
                }
            }
        }
        Block[][] location = getLocationList(arrFlag);
        int i = 0, j;
        LogUtils.e("start+" + TimeUtil.getNowFormatYYYYMMDDHHmmss());
        while (i < 9) {
            j = 0;
            while (j < 9) {
                if (arrFlag[i][j] == 1) {
                    j += 1;
                } else {
                    if (arrAnswer[i][j] != 0) {
                        invalid[i][j].add(arrAnswer[i][j]);//这一步是为了保证在回溯时，将回溯的节点的值也设为非法，避免死循环
                    }
                    List<Integer> valid = new ArrayList<>();
                    for (int k = 0; k < 9; k++) {
                        int num = k + 1;
                        if (!invalid[i][j].contains(num)) {
                            valid.add(num);
                        }
                    }
                    int k = 0;
                    int length = valid.size();
                    while (k < length) {
                        int num = valid.get(k);
                        arrAnswer[i][j] = num;
                        if (checkRow(num, i) && checkColumn(num, j) && checkGong(num, i, j)) {
                            j += 1;
                            break;
                        } else {
                            invalid[i][j].add(num);
                            k += 1;
                        }
                    }
                    if (k >= length) {
                        arrAnswer[i][j] = 0;
                        invalid[i][j] = clone(invalid_init[i][j]);
                        Block block = location[i][j];
                        i = block.getX();
                        j = block.getY();
                    }
                }
            }
            i += 1;
        }
        LogUtils.e("end+" + TimeUtil.getNowFormatYYYYMMDDHHmmss());
        invalidate();
    }

    /**
     * 获取每个格子不能用的数字的数组
     *
     * @return 所求
     */
    private List<Integer>[][] getInvalidMat() {
        List<Integer>[][] arr = new ArrayList[9][9];
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (arrTitle[i][j] == 0) {
                    List<Integer> list = new ArrayList<>();
                    //row
                    for (int k = 0; k < 9; k++) {
                        int num = arrTitle[i][k];
                        if (!list.contains(num) && num != 0) {
                            list.add(num);
                        }
                    }
                    //column
                    for (int k = 0; k < 9; k++) {
                        int num = arrTitle[k][j];
                        if (!list.contains(num) && num != 0) {
                            list.add(num);
                        }
                    }
                    //gong
                    int[] a = new int[9];
                    int index = 0;
                    int gongRow = i / 3;
                    int gongColumn = j / 3;
                    for (int m = gongRow * 3; m < gongRow * 3 + 3; m++) {
                        for (int n = gongColumn * 3; n < gongColumn * 3 + 3; n++) {
                            a[index] = arrTitle[m][n];
                            index++;
                        }
                    }
                    for (int k = 0; k < a.length; k++) {
                        int num = a[k];
                        if (!list.contains(num) && num != 0) {
                            list.add(num);
                        }
                    }
                    arr[i][j] = list;
                }
            }
        }
        return arr;
    }

    /**
     * 打印集合
     *
     * @param name 名称
     * @param list 集合
     */
    private void logList(String name, List list) {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        for (int i = 0; i < list.size(); i++) {
            sb.append(list.get(i)).append(",");
        }
        sb.append("]");
        LogUtils.e(name + ":" + sb.toString());
    }

    /**
     * 打印数组
     *
     * @param name 名称
     * @param arr  数组
     */
    private void logArr(String name, List[][] arr) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                sb.append("[");
                List<Integer> list = arr[i][j];
                if (Checker.hasList(list)) {
                    for (int k = 0; k < list.size(); k++) {
                        sb.append(list.get(k)).append(",");
                    }
                }
                sb.append("]");
                if ((j + 1) % 3 == 0) sb.append(" | ");
            }
            sb.append("\n");
            if ((i + 1) % 3 == 0) sb.append("\n");
        }
        sb.append("\n");
        LogUtils.e(name + "：\n" + sb.toString());
    }

    /**
     * 复制一个集合
     *
     * @param arrayList 集合
     * @return 所求
     */
    private List clone(List arrayList) {
        List newOne = new ArrayList();
        if (Checker.hasList(arrayList)) {
            newOne.addAll(arrayList);
        }
        return newOne;
    }

    /**
     * 检查横向是否满足不重复的条件
     *
     * @param number 数字
     * @param row    行数
     * @return 所求
     */
    private boolean checkRow(int number, int row) {
        if (number < 1 || number > 9) return false;
        if (row < 0 || row > 8) return false;
        int[] a = arrAnswer[row];
        boolean has = false;
        for (int i = 0; i < a.length; i++) {
            int num = a[i];
            if (num == 0) continue;
            if (num == number) {
                if (has) return false;
                else has = true;
            }
        }
        return true;
    }

    /**
     * 检查纵向是否满足不重复的条件
     *
     * @param number 数字
     * @param column 列数
     * @return 所求
     */
    private boolean checkColumn(int number, int column) {
        if (number < 1 || number > 9) return false;
        if (column < 0 || column > 8) return false;
        int[] a = new int[9];
        for (int i = 0; i < arrAnswer.length; i++) {
            a[i] = arrAnswer[i][column];
        }
        boolean has = false;
        for (int i = 0; i < a.length; i++) {
            int num = a[i];
            if (num == 0) continue;
            if (num == number) {
                if (has) return false;
                else has = true;
            }
        }
        return true;
    }

    /**
     * 检查九宫格是否满足不重复的条件
     *
     * @param number 数字
     * @param row    行数
     * @param column 列数
     * @return 所求
     */
    private boolean checkGong(int number, int row, int column) {
        if (number < 1 || number > 9) return false;
        if (row < 0 || row > 8) return false;
        if (column < 0 || column > 8) return false;
        int[] a = new int[9];
        int index = 0;
        int gongRow = row / 3;
        int gongColumn = column / 3;
        for (int i = gongRow * 3; i < gongRow * 3 + 3; i++) {
            for (int j = gongColumn * 3; j < gongColumn * 3 + 3; j++) {
                a[index] = arrAnswer[i][j];
                index++;
            }
        }
        boolean has = false;
        for (int i = 0; i < a.length; i++) {
            int num = a[i];
            if (num == 0) continue;
            if (num == number) {
                if (has) return false;
                else has = true;
            }
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(colorWhite);
        if (blockLong <= 0) {
            blockLong = getWidth() / 11f;
        }
        //lines
        paint.setColor(colorGray);
        for (int i = 0; i < 10; i++) {
            paint.setStrokeWidth(i % 3 == 0 ? 6 : 3);
            //rows
            canvas.drawLine(blockLong, blockLong * (i + 1), getWidth() - blockLong, blockLong * (i + 1), paint);
            //columns
            canvas.drawLine(blockLong * (i + 1), blockLong, blockLong * (i + 1), getHeight() - blockLong, paint);
        }
        //bg of current number
        if (current > 0) {
            paint.setColor(colorGray);
            canvas.drawRoundRect(current * blockLong, getHeight() - blockLong, (current + 1) * blockLong, getHeight(), RADIUS, RADIUS, paint);
        }
        //tags
        textPaint.setColor(colorBlack);
        textPaintBold.setColor(colorBlack);
        for (int i = 0; i < rows.length; i++) {
            //rows
            textPaint.getTextBounds(rows[i], 0, rows[i].length(), rect);
            canvas.drawText(rows[i], blockLong / 2f - rect.width() / 2f, blockLong * (i + 1) + blockLong / 2f - (rect.bottom + rect.top) / 2f, textPaint);
            //columns
            String strColumn = String.valueOf(i + 1);
            textPaint.getTextBounds("4", 0, strColumn.length(), rect);
            canvas.drawText(strColumn, blockLong * (i + 1) + blockLong / 2f - rect.width() / 2f, blockLong / 2f - (rect.bottom + rect.top) / 2f, textPaint);
            textPaintBold.getTextBounds("4", 0, strColumn.length(), rect);
            canvas.drawText(strColumn, blockLong * (i + 1) + blockLong / 2f - rect.width() / 2f, getHeight() - blockLong / 2f - (rect.bottom + rect.top) / 2f, textPaintBold);
        }
        //btn
        String start = "破解";
        textPaint.getTextBounds(start, 0, start.length(), rect);
        canvas.drawText(start, getWidth() - blockLong + blockLong / 2f - rect.width() / 2f, blockLong / 2f - (rect.bottom + rect.top) / 2f, textPaint);
        //numbers
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                int number = arrTitle[i][j];
                if (number != 0) {//固定的题目已给的数字
                    textPaintBold.setColor(colorBlack);
                    String str = String.valueOf(number);
                    textPaintBold.getTextBounds(str.length() == 1 ? "4" : str, 0, str.length(), rect);
                    if (number == current)
                        canvas.drawRoundRect(blockLong * (j + 1), blockLong * (i + 1), blockLong * (j + 2), blockLong * (i + 2), RADIUS, RADIUS, paint);
                    canvas.drawText(str, blockLong * (j + 1) + blockLong / 2f - rect.width() / 2f, blockLong * (i + 1) + blockLong / 2f - (rect.bottom + rect.top) / 2f, textPaintBold);
                } else {//解答的数字，可以更改
                    int number2 = arrAnswer[i][j];
                    if (number2 != 0) {
                        textPaintBold.setColor(colorBlue);
                        String str = String.valueOf(number2);
                        textPaintBold.getTextBounds(str.length() == 1 ? "4" : str, 0, str.length(), rect);
                        if (number2 == current)
                            canvas.drawRoundRect(blockLong * (j + 1), blockLong * (i + 1), blockLong * (j + 2), blockLong * (i + 2), RADIUS, RADIUS, paint);
                        canvas.drawText(str, blockLong * (j + 1) + blockLong / 2f - rect.width() / 2f, blockLong * (i + 1) + blockLong / 2f - (rect.bottom + rect.top) / 2f, textPaintBold);
                    }
                }
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        if (!editMode) return true;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                return true;
            case MotionEvent.ACTION_UP:
                if (y < blockLong) {//选择了第一行的按钮
                    if (x > getWidth() - blockLong) {
                        figureOut();
                    }
                    return true;
                } else if (y > getHeight() - blockLong) {//选择了某个数字将要填入
                    current = (int) (x / blockLong);
                    if (current < 1 || current > 9) return true;
//                    LogUtils.e("current = " + current);
                } else {//选择了某个格子，如果选择了数字则填入，否则提示先选择一个数字
                    if (current == 0) {
                        ToastUtils.getInstance().showInfo("请先选择一个数字");
                        return true;
                    }
                    int column = (int) (x / blockLong) - 1;
                    int row = (int) (y / blockLong) - 1;
                    if (column < 0) column = 0;
                    if (column > 8) column = 8;
                    if (row < 0) row = 0;
                    if (row > 8) row = 8;
//                    LogUtils.e("row_column = " + row + "_" + column);
                    if (arrAnswer[row][column] == 0) {
                        arrAnswer[row][column] = current;
                    } else {
                        arrAnswer[row][column] = 0;
                    }
                }
                invalidate();
                return true;
        }
        return super.onTouchEvent(event);
    }
}
